package com.tianji.learning.controller.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.controller.service.IPointsRecordService;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author zt
 * @since 2024-09-16
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {
    private final RedisTemplate redisTemplate;

    /**
     * 保存积分记录到数据库
     * @param msg 积分信息，包括用户id和获得积分
     * @param type 积分类型
     */
    @Override
    public void addPointRecord(SignInMessage msg, PointsRecordType type) {
        //0、校验msg中的用户id和积分数量有没有缺失
        if(msg.getPoints()==null||msg.getUserId()==null){
            //mq不能去throw，throw之后会继续重试，所以使用return，return处理结束了，不会触发重试机制
            return;
        }
        //创建一个变量用户保存实际增加的积分
        //因为如果已经达到上限的话，mq中传来的积分并不是最终新增的积分数据
        int realPoints = msg.getPoints();
        //1、获取积分类型的积分上限
        //如果积分上限的值为0就表示没有积分上限，如果是一个大于0的值就表示有积分上限，且积分上限为该值
        int maxPoints = type.getMaxPoints();
        if(maxPoints>0){
            //2、说明有积分上限，要查询数据库获取今日已获得积分
            //查询条件：userId，今日：createTime在今日开始和结束时间之间，积分类型
            //使用LocalDateTime.now()获取当前时间
            LocalDateTime now = LocalDateTime.now();
            //使用DateUtils获取今日的开始和结束时间
            LocalDateTime startTime = DateUtils.getDayStartTime(now);
            LocalDateTime endTime = DateUtils.getDayEndTime(now);
            //使用mybtis-plus查询 select sum(points) as totalPoints from points_record where user_id = xx and type = xx and create_time between (XX,XX)
            List<PointsRecord> list = this.lambdaQuery()
                    .select(PointsRecord::getPoints)
                    .eq(PointsRecord::getUserId, msg.getUserId())
                    .eq(PointsRecord::getType, type)
                    .between(PointsRecord::getCreateTime, startTime, endTime)
                    .list();
            int sumPoints = 0;
            for (PointsRecord pointsRecord : list) {
                sumPoints+=pointsRecord.getPoints();
            }
            //判断积分是否超过上限
            if(sumPoints>=maxPoints){
                //积分超过上限则直接返回，不用再保存数据
                return;
            }
            if(sumPoints+realPoints>=maxPoints){
                realPoints = maxPoints - sumPoints ;
            }
        }
        //3、保存积分
        //创建积分记录对象
        PointsRecord pointsRecord = new PointsRecord();
        pointsRecord.setUserId(msg.getUserId());
        pointsRecord.setPoints(realPoints);
        pointsRecord.setType(type);
        pointsRecord.setCreateTime(LocalDateTime.now());
        this.save(pointsRecord);
        //5.累加并保存总积分值到redis 采用zset 当前赛季排行榜
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        //拼接key，key是前缀+年月，表示是本月的key，用于生成本月的排行榜
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + format;
        //incrementScore表示是在原有的积分基础上累加积分
        redisTemplate.opsForZSet().incrementScore(key,msg.getUserId().toString(),realPoints);
    }

    /**
     * 查询我的今日积分，返回的是每个积分业务获得了多少积分
     * @return
     */
    @Override
    public List<PointsStatisticsVO> queryMyTodayPoints() {
        //1、获取用户id，因为要查询的是该用户的今日的积分，userId是查询数据库的条件之一
        Long userId = UserContext.getUser();
        //2、查询数据库表，points_record 条件：userId，今日，按type分组 type，sum(points)
        //select type,sum(points) where user_id =? and create_time between XX and XX group by type
        //获取今日的开始和结束时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startTime = DateUtils.getDayStartTime(now);
        LocalDateTime endTime = DateUtils.getDayEndTime(now);
        QueryWrapper<PointsRecord> wrapper = new QueryWrapper<>();
        wrapper.select("type,sum(points)");
        wrapper.eq("user_id",userId);
        wrapper.between("create_time",startTime,endTime);
        wrapper.groupBy("type");
        List<PointsRecord> list = this.list(wrapper);
        if(CollUtils.isEmpty(list)){
            return CollUtils.emptyList();
        }
        //3、返回voList
        List<PointsStatisticsVO> voList = new ArrayList<>();
        for (PointsRecord pointsRecord : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(pointsRecord.getType().getDesc());//积分类型的中文
            vo.setMaxPoints(pointsRecord.getType().getMaxPoints());//积分类型上限
            vo.setPoints(pointsRecord.getPoints());//getPoints里面保存的是sum的结果
            voList.add(vo);
        }
        return voList;
    }
}
